package com.kocla.mz.viewdraghelperdemo;

import android.content.Context;
import android.graphics.Point;
import android.os.Build;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

/**
 * http://www.cnblogs.com/punkisnotdead/p/4724825.html
 * http://www.jianshu.com/p/5670a67f0b19
 * http://blog.csdn.net/shangmingchao/article/details/49643749
 * 自定义viewgroup的时候 要重写onInterceptTouchEvent和onTouchEvent 这2个方法 是非常麻烦的事情，好在谷歌后来
 * <p>
 * 推出了ViewDragHelper这个类。可以极大方便我们自定义viewgroup.
 * Created by admin on 2017/7/21.
 */

public class DragLayout extends LinearLayout {
    private ViewDragHelper mDragger;

    private ViewDragHelper.Callback callback;

    private ImageView iv1;
    private ImageView iv2;
    private View mCapturedChild;

    private Point initPointPosition = new Point();

    private int mDragedIndex;
    //拖动距离小于300则回弹
    private static final int KICK_BACK_DINSTANCE = 300;

    public DragLayout(Context context) {
        this(context, null);
    }

    public DragLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DragLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        callback = new DraggerCallBack();
        //第二个参数就是滑动灵敏度的意思 可以随意设置
        mDragger = ViewDragHelper.create(this, 1.0f, callback);
        //注意如果需要使用边界检测需要添加上
        mDragger.setEdgeTrackingEnabled(ViewDragHelper.EDGE_ALL);
        setChildrenDrawingOrderEnabled(true);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public DragLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    protected void onFinishInflate() {
        iv1 = (ImageView) this.findViewById(R.id.iv1);
        iv2 = (ImageView) this.findViewById(R.id.iv2);
        super.onFinishInflate();

    }


    class DraggerCallBack extends ViewDragHelper.Callback {

        //TODO  tryCaptureView:控制哪些子view可以被拖动

        //这个地方实际上函数返回值为true就代表child可以滑动 为false 则不能滑动
        @Override
        public boolean tryCaptureView(View child, int pointerId) {
//            if (child == iv2) {//iv2不能移动
//                return false;
//            }
            return true;
        }


//        @Override
//        public int clampViewPositionHorizontal(View child, int left, int dx) {
//            return left;//这个地方实际上left就代表 你将要移动到的位置的坐标。返回值就是最终确定的移动的位。直接返回left时，滑动的时候我们的view会出了屏幕的边界。可以修改2个函数控制滑动的范围。
//        }
//        @Override
//        public int clampViewPositionVertical(View child, int top, int dy) {
//            return top;//类同clampViewPositionHorizontal
//        }


        //TODO  clampViewPositionHorizontal/clampViewPositionVertical控制拖动边界
        //限制被拖动的子View在水平方向的移动clampViewPositionHorizontal(View child, int left, int dx).（默认不允许水平移动，子类必须覆写这个方法并提供所需的夹紧值）
        //这个地方实际上left就代表 你将要移动到的位置的坐标。返回值就是最终确定的移动的位置。
        //我们要让view滑动的范围在我们的layout之内
        //实际上就是判断如果这个坐标在layout之内 那我们就返回这个坐标值。
        //如果这个坐标在layout的边界处 那我们就只能返回边界的坐标给他。不能让他超出这个范围
        //除此之外就是如果你的layout设置了padding的话，也可以让子view的活动范围在padding之内的.
        @Override
        public int clampViewPositionHorizontal(View child, int left, int dx) {
            //取得左边界的坐标
            final int leftBound = getPaddingLeft();
            //取得右边界的坐标
            final int rightBound = getWidth() - child.getWidth() - leftBound;
            //这个地方的含义就是 如果left的值 在leftbound和rightBound之间 那么就返回left
            //如果left的值 比leftbound还要小 那么就说明 超过了左边界 那我们只能返回给他左边界的值
            //如果left的值 比rightbound还要大 那么就说明 超过了右边界，那我们只能返回给他右边界的值
            return Math.min(Math.max(left, leftBound), rightBound);//和leftBound比取大值，再和rightbound比取小值
        }

        @Override
        public int clampViewPositionVertical(View child, int top, int dy) {
            final int topBound = getPaddingTop();
            final int bottomBound = getHeight() - child.getHeight() - topBound;
            return Math.min(Math.max(top, topBound), bottomBound);
        }

        //TODO onViewReleased 再加上一个回弹的效果
        //手指抬起释放时回调
        @Override
        public void onViewReleased(View releasedChild, float xvel, float yvel) {
            //松手的时候 判断如果是这个view 就让他回到起始位置
            int kickBackDinstance = (int) Math.hypot(Math.abs(offsety), Math.abs(offsetx));
            Log.i("onViewReleased", Math.abs(offsety) + "");
            if (releasedChild == iv1 && kickBackDinstance < KICK_BACK_DINSTANCE) {
                //这边代码你跟进去去看会发现最终调用的是startScroll这个方法 所以我们就明白还要在computeScroll方法里刷新
                //http://blog.csdn.net/wxx614817/article/details/52947092
                mDragger.settleCapturedViewAt(initPointPosition.x, initPointPosition.y);//还原到初始位置
                invalidate();
            }
        }


        //TODO getViewHorizontalDragRange/getViewVerticalDragRange 控制移动事件范围，解决imageview点击事件和拖动事件的冲突
        //给imageview设置点击事件后无法移动了, 如果你学过android 事件传递的话很好理解，因为如果你子view可以响应点击事件的话，那说明你消费了这个事件。
        //如果子View不能水平移动，应该返回0
        @Override
        public int getViewHorizontalDragRange(View child) {
            return getMeasuredWidth() - child.getMeasuredWidth();
        }

        //如果子View不能竖直移动，应该返回0
        @Override
        public int getViewVerticalDragRange(View child) {
            return getMeasuredHeight() - child.getMeasuredHeight();
        }

        //TODO 边界检测相关
        //触摸到边界 的时候调用
        @Override
        public void onEdgeTouched(int edgeFlags, int pointerId) {
            super.onEdgeTouched(edgeFlags, pointerId);
        }

        //返回true会锁住当前的边界,锁定后的边缘就不会回调onEdgeDragStarted() http://blog.csdn.net/wxx614817/article/details/52947092
        @Override
        public boolean onEdgeLock(int edgeFlags) {
            return super.onEdgeLock(edgeFlags);
        }

        int offsety, offsetx;

        @Override
        public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
            super.onViewPositionChanged(changedView, left, top, dx, dy);
            offsety += dy;
            offsetx += dx;
        }

        //当captureView被捕获的时候调用
        @Override
        public void onViewCaptured(View capturedChild, int activePointerId) {
            super.onViewCaptured(capturedChild, activePointerId);
            mCapturedChild = capturedChild;
            invalidate();
        }

        @Override
        public int getOrderedChildIndex(int index) {
            return super.getOrderedChildIndex(index);
        }
    }


    @Override
    public void computeScroll() {
        if (mDragger.continueSettling(true)) {//http://blog.csdn.net/wxx614817/article/details/52947092
            invalidate();
        }
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        //布局完成的时候就记录一下位置
        initPointPosition.x = iv1.getLeft();
        initPointPosition.y = iv1.getTop();

    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        //决定是否拦截当前事件
        return mDragger.shouldInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //处理事件
        mDragger.processTouchEvent(event);
        return true;
    }


    //重写getChildDrawingOrder使被拖动的view始终在最上面
    @Override
    protected int getChildDrawingOrder(int childCount, int i) {
        int index = indexOfChild(mCapturedChild);
        if (index == -1) {
            return i;
        }
        if (index == i) {
            mDragedIndex = i;
            return childCount - 1;
        } else if (childCount - 1 == i) {
            return mDragedIndex;
        } else {
            return i;
        }
    }
}
